package com.seven.ssa.controller;

import cn.dev33.satoken.annotation.SaCheckRole;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.jd.platform.hotkey.client.callback.JdHotKeyStore;
import com.seven.ssa.annotation.AuthCheck;
import com.seven.ssa.common.BaseResponse;
import com.seven.ssa.common.DeleteRequest;
import com.seven.ssa.common.ErrorCode;
import com.seven.ssa.common.ResultUtils;
import com.seven.ssa.exception.BusinessException;
import com.seven.ssa.model.dto.question.QuestionQueryRequest;
import com.seven.ssa.model.dto.questionBank.QuestionBankAddRequest;
import com.seven.ssa.model.dto.questionBank.QuestionBankQueryRequest;
import com.seven.ssa.model.dto.questionBank.QuestionBankUpdateRequest;
import com.seven.ssa.model.entity.Question;
import com.seven.ssa.model.entity.QuestionBank;
import com.seven.ssa.model.vo.QuestionBankVO;
import com.seven.ssa.model.vo.QuestionVO;
import com.seven.ssa.service.QuestionBankService;
import com.seven.ssa.service.QuestionService;
import com.seven.ssa.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 题库接口
 */
@RestController
@RequestMapping("/question/bank")
public class QuestionBankController {
    @Resource
    private QuestionBankService questionBankService;
    @Resource
    private QuestionService questionService;
    @Resource
    private UserService userService;

    /**
     * 创建题库
     *
     * @param questionBankAddRequest
     * @param request
     * @return
     */
    @PostMapping("/add")
    @SaCheckRole("admin")
    public BaseResponse<Long> addQuestionBank(@RequestBody QuestionBankAddRequest questionBankAddRequest, HttpServletRequest request) {
        if (questionBankAddRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 设置userId列
        questionBankAddRequest.setUserId(userService.getLoginUser(request).getId());
        QuestionBank questionBank = new QuestionBank();
        BeanUtils.copyProperties(questionBankAddRequest, questionBank);
        boolean result = questionBankService.save(questionBank);
        if (!result) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR);
        }
        return ResultUtils.success(questionBank.getId());
    }

    /**
     * 删除题库
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @PostMapping("/delete")
    @SaCheckRole("admin")
    public BaseResponse<Boolean> deleteQuestionBank(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        boolean b = questionBankService.removeById(deleteRequest.getId());
        return ResultUtils.success(b);
    }

    /**
     * 更新题库
     *
     * @param questionBankUpdateRequest
     * @param request
     * @return
     */
    @PostMapping("/update")
    @SaCheckRole("admin")
    public BaseResponse<Boolean> updateQuestionBank(@RequestBody QuestionBankUpdateRequest questionBankUpdateRequest, HttpServletRequest request) {
        if (questionBankUpdateRequest == null || questionBankUpdateRequest.getId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 设置userId列
        questionBankUpdateRequest.setUserId(userService.getLoginUser(request).getId());
        QuestionBank questionBank = new QuestionBank();
        BeanUtils.copyProperties(questionBankUpdateRequest, questionBank);
        boolean result = questionBankService.updateById(questionBank);
        return ResultUtils.success(result);
    }

    /**
     * 根据 id 获取题库
     *
     * @param id
     * @param request
     * @return
     */
    @GetMapping("/get")
    @SaCheckRole("admin")
    public BaseResponse<QuestionBankVO> getQuestionBankById(Long id, HttpServletRequest request) {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        QuestionBank questionBank = questionBankService.getById(id);
        QuestionBankVO questionBankVO = new QuestionBankVO();
        BeanUtils.copyProperties(questionBank, questionBankVO);
        return ResultUtils.success(questionBankVO);
    }

    /**
     * 获取题库列表
     *
     * @param questionBankQueryRequest
     * @param request
     * @return
     */
    @GetMapping("/list")
    @SaCheckRole("admin")
    public BaseResponse<List<QuestionBankVO>> listQuestionBank(QuestionBankQueryRequest questionBankQueryRequest, HttpServletRequest request) {
        QuestionBank questionBankQuery = new QuestionBank();
        if (questionBankQueryRequest != null) {
            BeanUtils.copyProperties(questionBankQueryRequest, questionBankQuery);
        }
        QueryWrapper<QuestionBank> queryWrapper = new QueryWrapper<>(questionBankQuery);
        List<QuestionBank> questionBankList = questionBankService.list(queryWrapper);
        List<QuestionBankVO> questionBankVOList = questionBankList.stream().map(questionBank -> {
            QuestionBankVO questionBankVO = new QuestionBankVO();
            BeanUtils.copyProperties(questionBank, questionBankVO);
            return questionBankVO;
        }).collect(Collectors.toList());
        return ResultUtils.success(questionBankVOList);
    }

    /**
     * 分页获取题库列表
     *
     * @param questionBankQueryRequest
     * @param request
     * @return
     */
    @GetMapping("/list/page")
    @SaCheckRole("admin")
    @SentinelResource(value = "listQuestionBankByPage",
            blockHandler = "handleBlockException",
            fallback = "handleFallback")//P7：单个接口限流
    public BaseResponse<Page<QuestionBankVO>> listQuestionBankByPage(QuestionBankQueryRequest questionBankQueryRequest, HttpServletRequest request) {
        long current = 1;
        long size = 10;
        QuestionBank questionBankQuery = new QuestionBank();
        if (questionBankQueryRequest != null) {
            BeanUtils.copyProperties(questionBankQueryRequest, questionBankQuery);
            current = questionBankQueryRequest.getCurrent();
            size = questionBankQueryRequest.getPageSize();
        }
        QueryWrapper<QuestionBank> queryWrapper = new QueryWrapper<>(questionBankQuery);
        Page<QuestionBank> questionBankPage = questionBankService.page(new Page<>(current, size), queryWrapper);
        Page<QuestionBankVO> questionBankVOPage = new PageDTO<>(questionBankPage.getCurrent(), questionBankPage.getSize(), questionBankPage.getTotal());
        List<QuestionBankVO> questionBankVOList = questionBankPage.getRecords().stream().map(questionBank -> {
            QuestionBankVO questionBankVO = new QuestionBankVO();
            BeanUtils.copyProperties(questionBank, questionBankVO);
            return questionBankVO;
        }).collect(Collectors.toList());
        questionBankVOPage.setRecords(questionBankVOList);
        return ResultUtils.success(questionBankVOPage);
    }

    /**
     * 根据题库id，查询题库及题目
     * @param questionBankQueryRequest
     * @param request
     * @return
     */
    @GetMapping("/get/vo")
    public BaseResponse<QuestionBankVO> getQuestionBankVOById
            (QuestionBankQueryRequest questionBankQueryRequest, HttpServletRequest request) {
        if(questionBankQueryRequest==null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 从【题库查询请求】中获取【题库id】
        Long id = questionBankQueryRequest.getId();
        if(id<=0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // P5：检测和处置爬虫
        questionService.crawlerDetect(userService.getLoginUser(request).getId());
        // P4：判断是否是热门题目，如果是，则从数据库获取后，放入本地缓存，之后直接从本地缓存获取即可
        String key = "bank_detail_"+id;
        if(JdHotKeyStore.isHotKey(key)){
            Object obj = JdHotKeyStore.get(key);//从本地缓存，获取缓存值
            if(obj!=null){
                return ResultUtils.success((QuestionBankVO)obj);//若缓存中有值，直接返回即可，不用再去MySQL查询了
            }
        }
        // P4：结束
        // 从【题库id】中获取【题库】
        QuestionBank questionBank = questionBankService.getById(id);
        if(questionBank==null){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        // 从【题库】中获取【题库视图】
        QuestionBankVO questionBankVO = questionBankService.getQuestionBankVO(questionBank,request);
        // 判断是否需要查询题库下的题目
        if(questionBankQueryRequest.isNeedQueryQuestionList()){
            // 新建【题目查询请求】并设置其【题库id】
            QuestionQueryRequest questionQueryRequest = new QuestionQueryRequest();
            questionQueryRequest.setQuestionBankId(questionBankQueryRequest.getId());
            // 通过【题目查询请求】获取【题目页】再获取【题目视图页】
            Page<Question> questionPage = questionService.listQuestionByPage(questionQueryRequest);
            Page<QuestionVO> questionVOPage = questionService.getQuestionVOPage(questionPage, request);
            //设置【题库视图】的【题目页】为【题目视图页】
            questionBankVO.setQuestionPage(questionVOPage);
        }
        // P4：设置本地缓存
        JdHotKeyStore.smartSet(key,questionBankVO);
        // 返回【题库视图】
        return ResultUtils.success(questionBankVO);
    }
    /**
     * P7：降级操作：直接返回本地数据（对应@SentinelResource注解）
     */
    public BaseResponse<Page<QuestionBankVO>> handleFallback(@RequestBody QuestionBankQueryRequest questionBankQueryRequest,
                                                             HttpServletRequest request, Throwable ex) {
        // 可以返回本地数据或空数据
        return ResultUtils.success(null);
    }

    /**
     * P7：限流操作：提示“系统压力过大，请耐心等待”（对应@SentinelResource注解）
     */
    public BaseResponse<Page<QuestionBankVO>> handleBlockException(@RequestBody QuestionBankQueryRequest questionBankQueryRequest,
                                                                   HttpServletRequest request, BlockException ex) {
        // 降级操作
        if(ex instanceof DegradeException) {
            return handleFallback(questionBankQueryRequest, request, ex);
        }
        // 限流操作
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统压力过大，请耐心等待");
    }

}
